/*
 * requests_list.c
 * This file is part of gURL
 *
 * Copyright (C) 2013, 2014 - Aleksey Konovalov
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Library General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "requests_list.h"

#include "defines.h"
#include <glib/gi18n.h>
#include "request_viewer_form.h"
#include "session.h"
#include "utils.h"

/* Signals */
enum {
    SELECT_SESSION,
    LAST_SIGNAL
};

struct _RequestsList {
    GtkGrid parent;

    GtkWidget* sessions_sw;
    gboolean sessions_added;
    GtkListStore* list_store;
    GHashTable* reqtbl;
};

struct _RequestsListClass {
    GtkGridClass parent_class;

    guint signals[LAST_SIGNAL];

    GdkPixbuf *green_icon, *red_icon, *gray_icon;
};

enum {
    COLUMN_STATUS_ICON,
    COLUMN_URL,
    COLUMN_HTTP_STATUS,
    COLUMN_REQUEST_INFO,
    COLUMNS_COUNT
};

#define STATUS_ICON_SIZE 24

G_DEFINE_TYPE(RequestsList, requests_list, GTK_TYPE_GRID)

static void requests_list_dispose(GObject* object);
static void on_item_selected(GtkTreeView* tree_view,
                             RequestsList* rl);

#define LOAD_ICON(res, name) \
{ \
    GError* error = NULL; \
    (res) = gdk_pixbuf_new_from_file_at_size( \
        GURL_DATA_DIR "/pixmaps/" name, \
        STATUS_ICON_SIZE, STATUS_ICON_SIZE, &error); \
    if(!res) { \
        g_warning("Error loading image file \"" name "\" [%d]: %s", \
                  error->code, error->message); \
        g_error_free(error); \
    } \
}


static void requests_list_class_init(RequestsListClass* klass) {
    G_OBJECT_CLASS(klass)->dispose = requests_list_dispose;

    klass->signals[SELECT_SESSION] =
        g_signal_new("select-session", REQUESTS_LIST_TYPE, G_SIGNAL_RUN_FIRST,
                     0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER);

    LOAD_ICON(klass->green_icon, "circle_green.svg");
    LOAD_ICON(klass->red_icon, "circle_red.svg");
    LOAD_ICON(klass->gray_icon, "circle_gray.svg");
}

static Session* get_selected_session(GtkTreeView* tree_view, RequestsList* rl) {
    Session* result = NULL;

    GtkTreePath* path = NULL;
    gtk_tree_view_get_cursor(tree_view, &path, NULL);
    if(!path)
        return NULL;

    int* idx = gtk_tree_path_get_indices(path);
    if(!idx) {
        fprintf(stderr, _("ERROR: cannot get selected element index"));
        goto cleanup;
    }

    GtkTreeIter iter;
    gboolean res = gtk_tree_model_iter_nth_child(
                        GTK_TREE_MODEL(rl->list_store), &iter, NULL, *idx);
    if(!res) {
        fprintf(stderr, _("ERROR: cannot get element #%d from requests list\n"), *idx);
        goto cleanup;
    }

    gtk_tree_model_get(GTK_TREE_MODEL(rl->list_store), &iter,
                       COLUMN_REQUEST_INFO, &result, -1);

cleanup:
    gtk_tree_path_free(path);
    return result;
}

static void store_session_data(Session* session,
                               const GString* (*get_data_fn)(Session*),
                               const char* dialog_title,
                               const char* file_default_name) {
    const GString* data = get_data_fn(session);
    show_save_dlg_and_save_data(data->str, data->len, dialog_title,
                                file_default_name);
}

static void on_store_request_hdrs_clicked(GtkWidget* UNUSED(menuitem),
                                          Session* session) {
    store_session_data(session, session_get_request_headers,
                       _("Save request headers as..."),
                       _("request_headers"));
}

static void on_store_request_body_clicked(GtkWidget* UNUSED(menuitem),
                                          Session* session) {
    store_session_data(session, session_get_request_body,
                       _("Save request body as..."),
                       _("request_body"));
}

static void on_store_response_hdrs_clicked(GtkWidget* UNUSED(menuitem),
                                           Session* session) {
    store_session_data(session, session_get_response_headers,
                       _("Save response headers as..."),
                       _("response_headers"));
}

static void on_store_response_body_clicked(GtkWidget* UNUSED(menuitem),
                                           Session* session) {
    store_session_data(session, session_get_response_body,
                       _("Save response body as..."),
                       _("response_body"));
}

static void view_popup_menu(GtkWidget* treeview, GdkEventButton* event,
                            RequestsList* rl) {
    Session* session = get_selected_session(GTK_TREE_VIEW(treeview), rl);
    if(session) {
        GtkWidget* menu = gtk_menu_new();

        GtkWidget* menuitem = gtk_menu_item_new_with_label(_("Save request headers"));
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
        g_signal_connect(menuitem, "activate",
                         (GCallback)on_store_request_hdrs_clicked,
                         session);

        menuitem = gtk_menu_item_new_with_label(_("Save request body"));
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
        g_signal_connect(menuitem, "activate",
                         (GCallback)on_store_request_body_clicked,
                         session);

        gtk_menu_shell_append(GTK_MENU_SHELL(menu),
                              gtk_separator_menu_item_new());

        menuitem = gtk_menu_item_new_with_label(_("Save response headers"));
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
        g_signal_connect(menuitem, "activate",
                         (GCallback)on_store_response_hdrs_clicked,
                         session);

        menuitem = gtk_menu_item_new_with_label(_("Save response body"));
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
        g_signal_connect(menuitem, "activate",
                         (GCallback)on_store_response_body_clicked,
                         session);

        gtk_widget_show_all(menu);
        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
                       event ? event->button : 0,
                       gdk_event_get_time((GdkEvent*)event));
    }
}

static gboolean on_session_view_popup_menu(GtkWidget* treeview,
                                           gpointer data) {
    view_popup_menu(treeview, NULL, data);
    return TRUE;
}

static gboolean on_session_view_button_pressed(GtkWidget* treeview,
                                               GdkEventButton* event,
                                               gpointer userdata) {
    if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
        view_popup_menu(treeview, event, userdata);
        return TRUE;
    }
    return FALSE;
}

static void on_clear_clicked(GtkWidget* UNUSED(w), RequestsList* self) {
    requests_list_clear(self);
}

static void on_sessions_view_size_allocate(GtkWidget* widget,
                                           GdkRectangle* allocation,
                                           gpointer user_data) {
    RequestsList* rl = REQUESTS_LIST(user_data);
    if(rl->sessions_added) {
        rl->sessions_added = FALSE;
        GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(
                                GTK_SCROLLED_WINDOW(rl->sessions_sw));
        gtk_adjustment_set_value(adj,
            gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj));
    }
}

static void requests_list_init(RequestsList* self) {
    self->reqtbl = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                        (GDestroyNotify)g_object_unref, g_free);

    self->list_store = gtk_list_store_new(COLUMNS_COUNT,
                                          GDK_TYPE_PIXBUF, G_TYPE_STRING,
                                          G_TYPE_LONG, G_TYPE_POINTER);

    GtkWidget* sessions_view = gtk_tree_view_new_with_model(
                                    GTK_TREE_MODEL(self->list_store));
    gtk_widget_show(sessions_view);
    g_signal_connect(sessions_view, "popup-menu",
                     (GCallback)on_session_view_popup_menu, self);
    g_signal_connect(sessions_view, "button-press-event",
                     (GCallback)on_session_view_button_pressed, self);

    GtkCellRenderer* picrenderer = gtk_cell_renderer_pixbuf_new();
    GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes(
        "", picrenderer, "pixbuf", NULL, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(sessions_view), column);

    GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("URL"), renderer, "text",
                                                      COLUMN_URL, NULL);
    gtk_tree_view_column_set_expand(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(sessions_view), column);

    column = gtk_tree_view_column_new_with_attributes(
                _("Status"), renderer, "text", COLUMN_HTTP_STATUS, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(sessions_view), column);

    g_signal_connect(sessions_view, "cursor-changed",
                     G_CALLBACK(on_item_selected), self);

    gtk_widget_set_size_request(GTK_WIDGET(self), 160, -1);

    self->sessions_sw = add_to_scrolled_window(sessions_view);
    g_object_set(self->sessions_sw, "expand", TRUE, NULL);
    gtk_grid_attach(GTK_GRID(self), self->sessions_sw, 0, 0, 1, 1);
    g_signal_connect(G_OBJECT(sessions_view), "size-allocate", 
                     G_CALLBACK(on_sessions_view_size_allocate), self);

    GtkWidget* bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show(GTK_WIDGET(bbox));
    gtk_container_set_border_width(GTK_CONTAINER (bbox), 5);
    gtk_grid_attach(GTK_GRID(self), bbox, 0, 1, 1, 1);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);

    GtkWidget* clearbtn = gtk_button_new_with_mnemonic(_("Clear"));
    gtk_widget_show(clearbtn);
    g_signal_connect(G_OBJECT(clearbtn), "clicked",
                     G_CALLBACK(on_clear_clicked), self);

    gtk_container_add(GTK_CONTAINER(bbox), clearbtn);
}

static void on_item_selected(GtkTreeView* tree_view,
                             RequestsList* rl) {
    Session* session = get_selected_session(tree_view, rl);
    if(session)
        g_signal_emit(rl, REQUESTS_LIST_GET_CLASS(rl)->signals[SELECT_SESSION],
                      0, session);
}

void requests_list_dispose(GObject* object) {
   g_hash_table_remove_all(REQUESTS_LIST(object)->reqtbl);
}

static void on_session_update(Session* session, updated_information inf,
                              RequestsList* rl) {
    if(inf == uRESPONSE_STATUS) {
        GtkTreeIter* iter = g_hash_table_lookup(rl->reqtbl, session);
        if(iter) {
            int status = session_get_http_status(session);
            RequestsListClass* klass = REQUESTS_LIST_GET_CLASS(rl);
            GdkPixbuf* icon =
                (status == 0 || (status >= 400 && status < 600)) ?
                klass->red_icon : klass->green_icon;
            gtk_list_store_set(rl->list_store, iter,
                               COLUMN_STATUS_ICON, icon,
                               COLUMN_HTTP_STATUS, status,
                               -1);
        }
    }
}

void requests_list_append(RequestsList* rl, Session* session) {
    GtkTreeIter* iter = g_malloc(sizeof(GtkTreeIter));
    g_object_ref(session);
    g_hash_table_insert(rl->reqtbl, session, iter);
    g_signal_connect(session, "update", G_CALLBACK(on_session_update), rl);
    gtk_list_store_append(rl->list_store, iter);
    gtk_list_store_set(rl->list_store, iter,
        COLUMN_STATUS_ICON, REQUESTS_LIST_GET_CLASS(rl)->gray_icon,
        COLUMN_URL, session_get_request_url(session)->str,
        COLUMN_HTTP_STATUS, session_get_http_status(session),
        COLUMN_REQUEST_INFO, session,
        -1);
    rl->sessions_added = TRUE;
}

void requests_list_clear(RequestsList* rl) {
    gtk_list_store_clear(rl->list_store);
    g_signal_emit(rl, REQUESTS_LIST_GET_CLASS(rl)->signals[SELECT_SESSION],
                  0, NULL);
    g_hash_table_remove_all(rl->reqtbl);
}

GtkWidget* requests_list_new() {
    return g_object_new(REQUESTS_LIST_TYPE, NULL);
}
